import java.io.*;
import java.net.*;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class FileServer {

    //创建服务端套接字
    private ServerSocket tcpSocket = null;

    //主函数
    public static void main(String[] args) throws IOException {
        FileServer fileServer = new FileServer(2021);
        fileServer.start();
    }


    //绑定套接字端口号
    public FileServer(int port1 ) throws IOException{
        tcpSocket = new ServerSocket(port1);
    }

    //开始连接
    public void start() throws IOException {
        System.out.println("服务器启动\n");
        //创建线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while(true){
            //使用此socket与客户端进行交流
            Socket clientSocket = tcpSocket.accept();
            threadPool.submit(()->{
                processConnection(clientSocket);
            });
        }

    }

    //运行过程处理函数
    private void processConnection(Socket clientSocket) {
        //服务端根目录
        String rootDirectory = "C:\\Users\\L\\Desktop\\fenbushi\\homework\\root" ;
        //追踪当前目录
        File currentDirectory;
        currentDirectory = new File(rootDirectory);
        //判断目录是否正确
        if(!currentDirectory.exists() || !currentDirectory.isDirectory()){
            System.out.println("Invalid root directory.");
            System.exit(1);
        }
        System.out.printf("客户端IP地址%s:客户端端口号%d连接成功\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());

        //创建输入输出流并对客户输入进行响应
        try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {

            out.printf("客户端IP地址:%s 客户端端口号:%d 连接成功\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
            out.flush();
            String input;

            while((input = in.readLine()) != null){
                String[] command = input.split(" ");
                //	ls	服务器返回当前目录文件列表（<file/dir>	name	size）
                if(command[0].equals("ls")){
                    listFiles(out,currentDirectory);
                    continue;
                }
                //	cd  <dir>	进入指定目录（需判断目录是否存在，并给出提示）
                if(command[0].equals("cd")){
                    currentDirectory = changeDirectory(command,out,currentDirectory);
                    continue;
                }
                //  get  <file>	通过UDP下载指定文件，保存到客户端当前目录下
                if(command[0].equals("get")) {
                    getFile(command, out, clientSocket, currentDirectory);
                    continue;
                }
                //bye	断开连接，客户端运行完毕
                if(command[0].equals("bye")){
                    System.out.printf("客户端IP地址%s:客户端端口号%d客户端下线！\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    out.flush();
                    clientSocket.close();
                    break;
                }
                //如果没有上述指令，则提醒用户重新输入
                else{
                    out.printf("请输入正确指令!\n");
                    out.flush();
                    continue;
                }


            }
        }
        catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //ls 指令处理函数
    private void listFiles(PrintWriter printWriter,File currentDirectory){
        File[] files = currentDirectory.listFiles();
        if (files != null) {
            for (File file : files) {
                String type = file.isDirectory() ? "dir" : "file";
                printWriter.println("<" + type + ">" + "\t" + file.getName() + "\t" + file.length());
               // printWriter.flush();
            }
            printWriter.println(); // Output an empty line to indicate the end of the list.
        }
    }

    //cd 指令的处理函数
    private synchronized File changeDirectory(String[] command,PrintWriter printWriter,File Directory){
        if (command.length != 2) {
            printWriter.println("无法加载，请检查指令是否正确");
            printWriter.flush();
            return Directory;
        }

        String directoryName = command[1];


        // 如果目录名为 ".."，则返回上一级目录
        if (directoryName.equals("..")) {
            File parentDirectory = Directory.getParentFile();
            if (parentDirectory != null && !isAboveRootDirectory(Directory, parentDirectory)) {
                Directory = parentDirectory;
                printWriter.println("successfully change");
                return Directory;
            } else {
                printWriter.println("Cannot change to the parent directory.");
                return Directory;
            }
        }
        else  {
            File newDir = new File(Directory, directoryName);
            if(newDir.exists() && newDir.isDirectory()){
                Directory = newDir;
                printWriter.println("successfully change");
                return Directory;
            }

            else {
                printWriter.println("Directory does not exist.");
                printWriter.flush();
                return Directory;
            }
        }

    }
    // 检查是否在根目录之上
    private boolean isAboveRootDirectory(File currentDirectory, File parentDirectory) {
        String rootPath = "C:\\Users\\L\\Desktop\\fenbushi\\homework\\root";
        String currentPath = currentDirectory.getAbsolutePath();
        String parentPath = parentDirectory.getAbsolutePath();
        return currentPath.length() < rootPath.length() || !currentPath.startsWith(rootPath) || !parentPath.startsWith(rootPath);
    }
    //get 指令处理函数
    private void getFile(String[] command,PrintWriter printWriter,Socket clientSocket,File currentDirectory){
        if (command.length != 2) {
            printWriter.println("无法加载，请检查指令是否正确");
            printWriter.flush();
            return;
        }

        String fileName = command[1];
        File fileToGet = new File(currentDirectory, fileName);

        //判断文件是否存在
        if (fileToGet.exists() && fileToGet.isFile()) {
            printWriter.println("OK" + " " + fileToGet.getName() + " " + fileToGet.length());
            //文件传输函数
            sendFile(fileToGet, clientSocket.getInetAddress(), 2020,clientSocket);
        } else {
            printWriter.println("File not found.");
            printWriter.flush();
        }
    }

    //文件传输函数
    private void sendFile(File file, InetAddress clientAddress, int clientPort,Socket socket){
        try (FileInputStream fileInput = new FileInputStream(file);
             DatagramSocket udpSocket = new DatagramSocket()) {

            byte[] buffer = new byte[1024];
            int bytesRead;
            long totalBytesSent = 0;

            //需要等到客户端准备好再进行发送，避免导致传输失败
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String response;
            while ((response = in.readLine()) != null) {
                if (response.contains("准备接收")) {
                    break;
                }
            }

            //通过udp进行传输
            while ((bytesRead = fileInput.read(buffer)) != -1) {
                DatagramPacket packet = new DatagramPacket(buffer, bytesRead, clientAddress, clientPort);
                udpSocket.send(packet);
                totalBytesSent += bytesRead;
                TimeUnit.MICROSECONDS.sleep(1); // Add a small delay to control the send rate.
            }

            System.out.println("Sent " + totalBytesSent + " bytes via UDP.");
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

}
